#define DOHOME_LOG_LVL          DOHOME_LOG_LVL_DEBUG
#define DOHOME_LOG_TAG          "hal_light"

#include "dohome_hal_light.h"

#include <stdio.h>

#define ERROR           0
#define SUCCESS         1

DOHOME_UINT16_T _max_duty = 255; 


DOHOME_STATIC DOHOME_UINT16_T _duty_map(DOHOME_UINT16_T x, DOHOME_UINT16_T in_min, DOHOME_UINT16_T in_max, DOHOME_UINT16_T out_min, DOHOME_UINT16_T out_max)
{
    if (x < in_min){
        x = in_min;
    }
    if (x > in_max){
        x = in_max;
    }
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}


/************************************
 * DOHOME_LIGHT_PWM_SELF            *
 ************************************/
#include "dohome_hal_pwm.h"
DOHOME_UINT16_T pwm_pin_table[5] = {0};
#define _hal_pwm_self_init(channel, freq, duty)         dohome_hal_pwm_init(pwm_pin_table[channel], freq, duty)
#define _hal_pwm_self_start(channel)                    dohome_hal_pwm_start(pwm_pin_table[channel])
#define _hal_pwm_self_stop(channel)                     dohome_hal_pwm_stop(pwm_pin_table[channel])
#define _hal_pwm_self_set_duty(channel, duty)           dohome_hal_pwm_set_duty(pwm_pin_table[channel], duty)
#define _hal_pwm_self_set_max_duty(duty)                dohome_hal_pwm_set_max_duty(duty)


/************************************
 * DOHOME_LIGHT_IIC_BP5758D          *
 ************************************/
#include "dohome_hal_gpio.h"
DOHOME_UINT8_T _bp5758d_init = 0;
DOHOME_UINT8_T _bp5758d_scl_io = 0xFF;
DOHOME_UINT8_T _bp5758d_sda_io = 0xFF;
DOHOME_UINT8_T _bp5758d_rgb_max_max_currentent = 10;
DOHOME_UINT8_T _bp5758d_cw_max_max_currentent = 10;


// xSemaphoreHandle BP5758D_busy;

DOHOME_STATIC void BP5758D_gpio_init()
{
	// 上拉
	dohome_hal_gpio_init(_bp5758d_scl_io, DOHOME_GPIO_OUTPUT, DOHOME_GPIO_PULLHIGH);
    dohome_hal_gpio_init(_bp5758d_sda_io, DOHOME_GPIO_OUTPUT, DOHOME_GPIO_PULLHIGH);
    dohome_hal_gpio_write(_bp5758d_scl_io, 1);
    dohome_hal_gpio_write(_bp5758d_sda_io, 1);
}


DOHOME_STATIC void Simulate_BP5758D_Delay(void)
{       
	volatile DOHOME_UINT16_T j = 10;
	while(j)
	{
		j --;
	}
}


DOHOME_STATIC DOHOME_UINT8_T Simulate_BP5758D_Start()
{
	dohome_hal_gpio_init(_bp5758d_sda_io, DOHOME_GPIO_OUTPUT, DOHOME_GPIO_PULLHIGH);
	dohome_hal_gpio_write(_bp5758d_sda_io, 1);
	dohome_hal_gpio_write(_bp5758d_scl_io, 1);

	Simulate_BP5758D_Delay();
	dohome_hal_gpio_init(_bp5758d_sda_io, DOHOME_GPIO_INPUT, DOHOME_GPIO_PULLHIGH);

	if(!dohome_hal_gpio_read(_bp5758d_sda_io))
		return ERROR;                                                               

	dohome_hal_gpio_init(_bp5758d_sda_io, DOHOME_GPIO_OUTPUT, DOHOME_GPIO_PULLHIGH);
	dohome_hal_gpio_write(_bp5758d_sda_io, 0);
	Simulate_BP5758D_Delay();

	return SUCCESS;
}


DOHOME_STATIC void Simulate_BP5758D_Stop()
{
	dohome_hal_gpio_init(_bp5758d_sda_io, DOHOME_GPIO_OUTPUT, DOHOME_GPIO_PULLHIGH);
	dohome_hal_gpio_write(_bp5758d_scl_io, 0);
	Simulate_BP5758D_Delay();
	dohome_hal_gpio_write(_bp5758d_sda_io, 0);
	Simulate_BP5758D_Delay();
	dohome_hal_gpio_write(_bp5758d_scl_io, 1);
	Simulate_BP5758D_Delay();
	dohome_hal_gpio_write(_bp5758d_sda_io, 1);
	Simulate_BP5758D_Delay();
}


DOHOME_STATIC DOHOME_UINT8_T Simulate_BP5758D_Wait_Ack()
{
	dohome_hal_gpio_write(_bp5758d_scl_io, 0);
	Simulate_BP5758D_Delay();
	dohome_hal_gpio_write(_bp5758d_sda_io, 1);                     
	Simulate_BP5758D_Delay();
	dohome_hal_gpio_init(_bp5758d_sda_io, DOHOME_GPIO_INPUT, DOHOME_GPIO_PULLHIGH);
	dohome_hal_gpio_write(_bp5758d_scl_io, 1);
	Simulate_BP5758D_Delay();
	if(dohome_hal_gpio_read(_bp5758d_sda_io))
	{
		dohome_hal_gpio_write(_bp5758d_scl_io, 0);
		return ERROR;
	}
	
	dohome_hal_gpio_write(_bp5758d_scl_io, 0);
	return SUCCESS;
}


DOHOME_STATIC void Simulate_BP5758D_Send_Byte(DOHOME_UINT8_T SendByte)
{
	DOHOME_UINT8_T i = 8;
	dohome_hal_gpio_init(_bp5758d_sda_io, DOHOME_GPIO_OUTPUT, DOHOME_GPIO_PULLHIGH);
	while(i --)
	{	
		dohome_hal_gpio_write(_bp5758d_scl_io, 0);
		Simulate_BP5758D_Delay();

		if(SendByte & 0x80)
			dohome_hal_gpio_write(_bp5758d_sda_io, 1);  
		else
		    dohome_hal_gpio_write(_bp5758d_sda_io, 0);  

		SendByte <<= 1;
		Simulate_BP5758D_Delay();

		dohome_hal_gpio_write(_bp5758d_scl_io, 1);
		Simulate_BP5758D_Delay();  
	}
	dohome_hal_gpio_write(_bp5758d_scl_io, 0);
}


DOHOME_STATIC DOHOME_UINT8_T BP5758D_WritePage(DOHOME_UINT8_T *pBuffer, DOHOME_UINT16_T NumByteToWrite)
{ 
	if(!Simulate_BP5758D_Start())
	{
		return ERROR;
	}

	while(NumByteToWrite --)
	{
		Simulate_BP5758D_Send_Byte( *pBuffer ++);
		if(!Simulate_BP5758D_Wait_Ack()){
	        Simulate_BP5758D_Stop();
            return ERROR;
        }	
	}

	Simulate_BP5758D_Stop();
	
    // Simulate_BP5758D_Delay();

	return SUCCESS;
}

typedef struct{
    DOHOME_UINT16_T r;
    DOHOME_UINT16_T g;
    DOHOME_UINT16_T b;
    DOHOME_UINT16_T c;
    DOHOME_UINT16_T w;
}_rgbcw_t;

_rgbcw_t bp5758d_rgbcw = {0};


DOHOME_STATIC DOHOME_UINT8_T _iic_bp5758d_set_duty(dohome_light_channel_t channel, DOHOME_UINT32_T duty){
    DOHOME_UINT8_T BP5758D_value[3] = {0};

    duty = _duty_map(duty, 0, _max_duty, 0, 1023);

    switch (channel)
    {
    case 0:
        bp5758d_rgbcw.r = duty;
        BP5758D_value[0] = 0xA6;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.r & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.r & (0X03E0)) >> 5);
        break;
    case 1:
        bp5758d_rgbcw.g = duty;
        BP5758D_value[0] = 0xA8;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.g & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.g & (0X03E0)) >> 5);
        break;
    case 2:
        bp5758d_rgbcw.b = duty;
        BP5758D_value[0] = 0xAA;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.b & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.b & (0X03E0)) >> 5);
        break;
    case 3:
        bp5758d_rgbcw.c = duty;
        BP5758D_value[0] = 0xAC;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.c & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.c & (0X03E0)) >> 5);
        break;
    case 4:
        bp5758d_rgbcw.w = duty;
        BP5758D_value[0] = 0xAE;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.w & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.w & (0X03E0)) >> 5);
        break;
    default:
        break;
    }

    if(_bp5758d_init){
        return BP5758D_WritePage(BP5758D_value, 3);
    }
    return ERROR;
}

DOHOME_STATIC DOHOME_UINT8_T _iic_bp5758d_stop(DOHOME_UINT8_T channle){
    DOHOME_UINT8_T BP5758D_value[3] = {0};
    BP5758D_value[0] = 0xA0 + channle*2 + 6;
    BP5758D_value[1] = 0;
    BP5758D_value[2] = 0;
    return BP5758D_WritePage(BP5758D_value, 3);
}

DOHOME_STATIC DOHOME_UINT8_T _iic_bp5758d_start(DOHOME_UINT8_T channle){
    DOHOME_UINT8_T BP5758D_value[3] = {0};
    switch (channle)
    {
    case 0:
        BP5758D_value[0] = 0xA6;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.r & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.r & (0X03E0)) >> 5);
        break;
    case 1:
        BP5758D_value[0] = 0xA8;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.g & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.g & (0X03E0)) >> 5);
        break;
    case 2:
        BP5758D_value[0] = 0xAA;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.b & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.b & (0X03E0)) >> 5);
        break;
    case 3:
        BP5758D_value[0] = 0xAC;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.c & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.c & (0X03E0)) >> 5);
        break;
    case 4:
        BP5758D_value[0] = 0xAE;
        BP5758D_value[1] = (DOHOME_UINT8_T)(bp5758d_rgbcw.w & (0X001F));
	    BP5758D_value[2] = (DOHOME_UINT8_T)((bp5758d_rgbcw.w & (0X03E0)) >> 5);
        break;
    default:
        break;
    }

    if(_bp5758d_init){
        return BP5758D_WritePage(BP5758D_value, 3);
    }
    return ERROR;
}

DOHOME_UINT8_T BP5758D_INIT_PACK[17] = {0};

DOHOME_STATIC DOHOME_UINT8_T _light_add(dohome_light_cfg_t led_cfg, dohome_light_item_t item){

    switch (item.type)
    {
    case DOHOME_LIGHT_PWM_SELF:
        pwm_pin_table[item.channel] = item.cfg.pwm.io;
        _hal_pwm_self_init(item.channel, led_cfg.pwm.freq, 0);
        return SUCCESS;
        break;
    case DOHOME_LIGHT_IIC_BP5758D:
        if ((item.channel >= 5) | (item.cfg.iic.max_current >= 90)){
            return ERROR;
        }
        //使能对应通道
        // BP5758D_INIT_PACK[1] =BP5758D_INIT_PACK[1] | (0x01 << item.channel);    
        BP5758D_INIT_PACK[1] = 0x1f;    
        //设置对应通道量程
        BP5758D_INIT_PACK[item.channel+2] = item.cfg.iic.max_current;
        if(BP5758D_INIT_PACK[item.channel+2] > 63){
            BP5758D_INIT_PACK[item.channel+2] = (BP5758D_INIT_PACK[item.channel+2]-30) | 0x40;
        }
        return SUCCESS;
        break;
    default:
        break;
    }
    return ERROR;
}

dohome_op_ret dohome_hal_light_init(dohome_light_cfg_t led_cfg, dohome_light_item_t *item_list, DOHOME_UINT8_T item_num){

    DOHOME_UINT8_T add_ret = ERROR;
    dohome_light_type_t light_type = DOHOME_LIGHT_NULL;

    for (DOHOME_UINT8_T i = 0; i < item_num; i++)
    {
        add_ret = _light_add(led_cfg, item_list[i]);
        if(add_ret){
            light_type = light_type | item_list[i].type;
        }else{
            return OPRT_COM_ERROR;
        }
    }

    if(light_type == 0x0)   return OPRT_COM_ERROR;

    if(light_type & DOHOME_LIGHT_PWM_SELF){
        
    }

    if(light_type & DOHOME_LIGHT_IIC_BP5758D){
        if (led_cfg.iic.scl == led_cfg.iic.sda){
            return OPRT_COM_ERROR;
        }

        if(_bp5758d_init == 0){
            _bp5758d_init = 1;
            _bp5758d_scl_io = led_cfg.iic.scl;
            _bp5758d_sda_io = led_cfg.iic.sda;
            BP5758D_gpio_init();
            BP5758D_INIT_PACK[0] = 0xA0;
            if(!BP5758D_WritePage(BP5758D_INIT_PACK, 17))
                return OPRT_COM_ERROR;
        }
    }

    _max_duty = led_cfg.max_duty;

    if(light_type & DOHOME_LIGHT_PWM_SELF){
        _hal_pwm_self_set_max_duty(_max_duty);
    }
     
    return OPRT_COM_ERROR;
}


dohome_op_ret dohome_hal_light_item_stop(dohome_light_item_t item){
    switch (item.type)
    {
    case DOHOME_LIGHT_PWM_SELF:
        _hal_pwm_self_stop(item.channel);
        break;
    case DOHOME_LIGHT_IIC_BP5758D:
        if(_iic_bp5758d_stop(item.channel))
            return OPRT_OK;
        break;
    default:
        break;
    }
    return OPRT_COM_ERROR;
}


dohome_op_ret dohome_hal_light_item_start(dohome_light_item_t item){
    switch (item.type)
    {
    case DOHOME_LIGHT_PWM_SELF:
        _hal_pwm_self_start(item.channel);
        break;
    case DOHOME_LIGHT_IIC_BP5758D:
        if(_iic_bp5758d_start(item.channel))
            return OPRT_OK;
        break;
    default:
        break;
    }
    return OPRT_COM_ERROR;
}

dohome_op_ret dohome_hal_light_item_set_duty(dohome_light_item_t item, DOHOME_UINT32_T duty){
    switch (item.type)
    {
    case DOHOME_LIGHT_PWM_SELF:
        _hal_pwm_self_set_duty(item.channel, duty);
        break;
    case DOHOME_LIGHT_IIC_BP5758D:
        if(_iic_bp5758d_set_duty(item.channel, duty))
            return OPRT_OK;
        break;
    default:
        break;
    }
    return OPRT_COM_ERROR;
}